package com.iceland.server.server.proxy;

import com.iceland.common.listener.ConsumeListener;
import com.iceland.common.message.CmdMessage;
import com.iceland.common.message.MessageType;
import com.iceland.common.monitor.Info;
import com.iceland.common.monitor.InfoMontorable;
import com.iceland.common.util.AttributeKeyUtils;
import com.iceland.common.util.ChannelContext;
import com.iceland.server.config.Environment;
import com.iceland.server.util.ServerChannelContext;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MarkerFactory;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 处理真实客户端的请求
 * 转发收到的数据给代理客户端
 */
@Slf4j
public class ProxyHandler  extends ChannelInboundHandlerAdapter {
    ProxyServerManager proxyServerManager = ProxyServerManager.getSingleInstance();
    ProxyServer server;

    static final int timeout= Environment.getProxyConfig().getProxyConnectionTimeout();
//    public static final AtomicLong failureChannels = new AtomicLong();
//    public static final AtomicLong successChannels = new AtomicLong();



//    handlerAdded: 新建立的连接会按照初始化策略，把handler添加到该channel的pipeline里面，也就是channel.pipeline.addLast(new LifeCycleInBoundHandler)执行完成后的回调；
//    channelRegistered: 当该连接分配到具体的worker线程后，该回调会被调用。
//    channelActive：channel的准备工作已经完成，所有的pipeline添加完成，并分配到具体的线上上，说明该channel准备就绪，可以使用了。
//    channelRead：客户端向服务端发来数据，每次都会回调此方法，表示有数据可读；
//    channelReadComplete：服务端每次读完一次完整的数据之后，回调该方法，表示数据读取完毕；
//    channelInactive：当连接断开时，该回调会被调用，说明这时候底层的TCP连接已经被断开了。
//    channelUnREgistered: 对应channelRegistered，当连接关闭后，释放绑定的workder线程；
//    handlerRemoved： 对应handlerAdded，将handler从该channel的pipeline移除后的回调方法。

    //连接
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        log.info(MarkerFactory.getMarker(this.getClass().getTypeName()),"真实请求{}连接至{}",ctx.channel().remoteAddress(),ctx.channel().localAddress());
        /**
         * 取消autoRead
         */
        ctx.channel().config().setAutoRead(false);

        //1.获取当前端口
        int port = ((InetSocketAddress) ctx.channel().localAddress()).getPort();
        server = proxyServerManager.getServer(port);
        //2.获取可用连接

        boolean b=server.getChannelPool().getAvailableChannel(server, new ConsumeListener<Channel>(timeout) {
            @Override
            protected void consume(Channel availableChannel) {
                if(availableChannel==null){
                    log.error("已经没有可以使用的连接了");
//                    failureChannels.addAndGet(1);
                    ctx.close();
                    return;
                }

                log.debug(MarkerFactory.getMarker(this.getClass().getTypeName()),"获取到了可用的流量转发通道,localAddress:{},remoteAddress:{}",availableChannel.localAddress(),availableChannel.remoteAddress());
                //3.创建本通道上下文,并完成通道的相互绑定
                ServerChannelContext channelContext=new ServerChannelContext(server.getRpcMessageManager(), availableChannel,server,null);
                //给本通道添加上下文
                AttributeKeyUtils.setChannelContext(ctx.channel(), channelContext);

                //获取绑定的通道上下文
                ChannelContext availableChannelContext=AttributeKeyUtils.getChannelContext(availableChannel);
                //绑定本通道
                availableChannelContext.setBindChannel(ctx.channel());

                /**
                 * 启用autoRead
                 */
                ctx.channel().config().setAutoRead(true);
//                successChannels.addAndGet(1);
            }

            @Override
            protected void expire(Channel element) {
//                failureChannels.addAndGet(1);
                ctx.close();
            }
        });

        if(b==false){
            log.warn("超出可支持的最大连接数，如果需要并发支持更多连接，可修改配置文件proxy.yml中的maxProxyServerConnectionSize字段");
//            failureChannels.addAndGet(1);
            ctx.close();
        }
    }

    /**
     * 没有解码器解包的情况下，单次最多读取到2048字节数据
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        ChannelContext channelContext =AttributeKeyUtils.getChannelContext(ctx.channel());

        ByteBuf in=(ByteBuf)msg;
        int readableBytes=in.readableBytes();
        if(in.readableBytes()>0){
            CmdMessage cmdMessage=new CmdMessage(MessageType.DATA_EXCHANGE.getType(),null,in);
            //获取cmdChannel
            Channel bindChannel = channelContext.getBindChannel();
            bindChannel.writeAndFlush(cmdMessage);
            log.debug(MarkerFactory.getMarker(this.getClass().getTypeName()),"转发来自真实客户端的流量{}字节：代理服务端{}接收到消息，通过CMD服务端{}转发消息至CMD客户端{}",readableBytes,ctx.channel().localAddress(),bindChannel.localAddress(),bindChannel.remoteAddress());

        }

    }

//    @Override
//    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        log.info("channelReadComplete执行");
//    }

    //断开
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.debug(MarkerFactory.getMarker(this.getClass().getTypeName()),"代理通道数量总合：{}，正在使用的通道数:{},空闲的通道数:{},未处理的监听器：{}",server.getChannelPool().getChannelSize(),server.getChannelPool().getOccupationChannelSize(),server.getChannelPool().getAvailableChannelSize(),server.getChannelPool().getListnerSize());

        log.debug(MarkerFactory.getMarker(this.getClass().getTypeName()),"通道断开：真实客户端与代理服务端之间通道断开，真实客户端:{},代理服务端：{}",ctx.channel().remoteAddress(),ctx.channel().localAddress());
        ServerChannelContext channelContext = (ServerChannelContext)AttributeKeyUtils.getChannelContext(ctx.channel());
        if(channelContext!=null){
            if(channelContext.getBindChannel()!=null){
                channelContext.getBindChannel().close();
            }

            //释放代理通道
            Channel bindChannel = channelContext.getBindChannel();
            ProxyServer bindProxyServer = channelContext.getProxyServer();
            log.debug(MarkerFactory.getMarker(this.getClass().getTypeName()),"释放代理通道,CMD客户端:{},CMD服务端:{}",bindChannel.remoteAddress(),bindChannel.localAddress());
            bindProxyServer.getChannelPool().freeOccupationChannel(bindChannel);
//            log.info("代理通道数量总合：{}，正在使用的通道数:{},空闲的通道数:{},未处理的监听器：{}",bindProxyServer.getChannelPool().getChannelSize(),bindProxyServer.getChannelPool().getOccupationChannelSize(),bindProxyServer.getChannelPool().getAvailableChannelSize(),bindProxyServer.getChannelPool().getListnerSize());
        }
        ctx.fireChannelInactive();
    }


}
